/*******************************************************************************
 * Copyright (c) 2002 IBM Corporation and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors: IBM - Initial implementation Boris Pruessmann - reformatted
 * sourcecode, changed cvs to perforce
 ******************************************************************************/
package net.sourceforge.perforce.core.syncinfo;

import org.eclipse.core.runtime.Platform;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.internal.Policy;

/**
 * Provides a per-thread nested locking mechanism. A thread can acquire a lock
 * and then call acquire() multiple times. Other threads that try and acquire
 * the lock will be blocked until the first thread releases all it's nested
 * locks.
 * 
 * @version $Revision: 488 $
 * @author IBM
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class ReentrantLock {
  /** @see Platform#getDebugOption */
  static final String OPTION_DEBUG_LOCK = PerforceCorePlugin.ID + "/debug/lock"; //$NON-NLS-1$

  // ---------------------------------------------------------------------------
  // Private Variables

  /** Stores the reference to the thread that currently owns the lock. */
  private Thread thread;
  /** Stores the nesting count. */
  private int nestingCount;
  /** Tracing flag. */
  private boolean debugLock;

  // -------------------------------------------------------------------------
  // Public Constructors

  /** Constructor for ReentrantLock. */
  public ReentrantLock() {
    this.thread = null;
    this.nestingCount = 0;
    final String opt = Platform.getDebugOption(OPTION_DEBUG_LOCK);
    this.debugLock = Boolean.valueOf(opt).booleanValue();
  }

  //----------------------------------------------------------------------------
  // -- Public Methods

  /** Call this method to acquire the lock. */
  public synchronized void acquire() {
    Thread thisThread = Thread.currentThread();

    // stop early if we've been interrupted -- don't enter the lock anew
    if (thread != thisThread) {
      // race for access to the lock -- does not guarantee fairness
      while (nestingCount != 0) {
        try {
          wait();
        } catch (InterruptedException e) {
          Thread.yield(); // keep waiting for the lock
        }
      }

      thread = thisThread;
    }
    nestingCount++;
    log("lock acquired, nesting count: " + nestingCount);
  }

  /** Call this method to release the lock. */
  public synchronized void release() {
    Thread thisThread = Thread.currentThread();
    Policy.assertTrue(thread == thisThread);

    if (--nestingCount == 0) {
      thread = null;
      notifyAll();
    }
    log("lock released, nesting count: " + nestingCount);
  }

  /**
   * Call this method to retrieve the lock's nesting count.
   * 
   * @return the Lock's nesting count.
   */
  public int getNestingCount() {
    Thread thisThread = Thread.currentThread();
    Policy.assertTrue(thread == thisThread);

    return nestingCount;
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  private final void log(String s) {
    if (debugLock) {
      PerforceCorePlugin.logError(s, new Throwable());
    } // if
  } // log

}
